#include <linux/module.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/cdev.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/mutex.h>
#include <linux/wait.h>
#include <linux/sched/signal.h>
#include <linux/poll.h>


wait_queue_head_t simple_wait;

struct simple_epoll_dev
{
    struct cdev c_dev;
    struct mutex mtx;
    int current_len;
};
static struct simple_epoll_dev* simple_epoll_devp;
static dev_t devno;
static struct class* simple_epoll_cls;

static int simple_epoll_open(struct inode* inode, struct file* filp)
{
    filp->private_data = simple_epoll_devp;
    return 0;
}

static int simple_epoll_release(struct inode* inode, struct file* filp)
{
    return 0;
}


static ssize_t simple_epoll_write(struct file* filp, const char __user* buf, size_t size, loff_t* ppos)
{
    int len = size;
    struct simple_epoll_dev* dev = filp->private_data;

    dev->current_len += len;

    wake_up_interruptible(&simple_wait);    //wake up thread waiting to read

    return len;     //注意，不能return 0。否则echo "xxxx" >/dev/simple_epoll会一直尝试执行
}

int poll_cnt = 0;
static unsigned int simple_epoll_poll(struct file* filp, poll_table* wait)
{
    unsigned int mask = 0;
    struct simple_epoll_dev* dev = filp->private_data;
    printk("poll enter %d\n", poll_cnt);

    mutex_lock(&dev->mtx);
    poll_wait(filp, &simple_wait, wait);

    if (0  != dev->current_len)
    {
        mask |= POLLIN;
    }


    mutex_unlock(&dev->mtx);

    printk("poll exit %d\n", poll_cnt++);

    return mask;
}

static const struct file_operations simple_epoll_fops =
{
    .owner = THIS_MODULE,
    .write = simple_epoll_write,
    .open = simple_epoll_open,
    .release = simple_epoll_release,
    .poll = simple_epoll_poll,
};

static void simple_epoll_setup_cdev(struct simple_epoll_dev* dev, int index)
{
    int err;
    struct device* simple_epoll_device;

    cdev_init(&dev->c_dev, &simple_epoll_fops);
    dev->c_dev.owner = THIS_MODULE;
    err = cdev_add(&dev->c_dev, devno, 1);

    if (err)
    {
        printk(KERN_NOTICE "Error %d adding simple_epoll%d\n", err, index);
    }

    simple_epoll_cls = class_create(THIS_MODULE, "simple_epoll");
    simple_epoll_device = device_create(simple_epoll_cls, NULL, devno, NULL, "simple_epoll");
}

static int __init simple_epoll_init(void)
{
    int ret;

    ret = alloc_chrdev_region(&devno, 0, 1, "simple_epoll");
    if (ret < 0)
    {
        return ret;
    }

    simple_epoll_devp = kzalloc(sizeof(struct simple_epoll_dev), GFP_KERNEL);
    if (!simple_epoll_devp)
    {
        ret = -ENOMEM;
        goto fail_malloc;
    }

    mutex_init(&simple_epoll_devp->mtx);
    init_waitqueue_head(&simple_wait);

    simple_epoll_setup_cdev(simple_epoll_devp, 0);
    return 0;

fail_malloc:
    unregister_chrdev_region(devno, 1);
    return ret;
}
module_init(simple_epoll_init);

static void __exit simple_epoll_exit(void)
{
    device_destroy(simple_epoll_cls, devno);
    class_destroy(simple_epoll_cls);
    cdev_del(&simple_epoll_devp->c_dev);
    kfree(simple_epoll_devp);
    unregister_chrdev_region(devno, 1);
}
module_exit(simple_epoll_exit);

MODULE_LICENSE("GPL");
